From 3cffa2e1f9bf4efd0d3546bf279a20d18836dd43 Mon Sep 17 00:00:00 2001 From: Andrey Borodin Date: Sun, 24 Jul 2016 14:42:21 +0500 Subject: [PATCH] temp8 --- src/backend/access/gist/README | 8 +++ src/backend/access/gist/gist.c | 20 ++++++- src/backend/access/gist/gistxlog.c | 22 +++++++- src/backend/storage/page/bufpage.c | 106 +++++++++++++++++++++++++++++++++++++ src/include/storage/bufpage.h | 1 + 5 files changed, 153 insertions(+), 4 deletions(-) diff --git a/src/backend/access/gist/README b/src/backend/access/gist/README index dd4c9fa..b02247f 100644 --- a/src/backend/access/gist/README +++ b/src/backend/access/gist/README @@ -171,6 +171,14 @@ pages. The caller must then call gistplacetopage() on the parent page to insert the downlink tuples. The parent page that holds the downlink to the child might have migrated as a result of concurrent splits of the parent, gistfindCorrectParent() is used to find the parent page. +gistplacetopage gets list of tuples and offnum as a parametes. If passed +offnum is invalid, all tuples will be apended at the end of a page. If list +contains many tuples and offnum is valid, offnum`ed tuple will be deleted from +the page and all tuples from list appended at the end of a page. Append is +performed by PageAddItem routine. If list contains exactly one tuple and offnum + is valid, tuple on page is replaced with PageIndexTupleOverwrite routine, +which shows significant performance improvement in case when old tuple's size +matches size of a new tuple. Splitting the root page works slightly differently. At root split, gistplacetopage() allocates the new child pages and replaces the old root diff --git a/src/backend/access/gist/gist.c b/src/backend/access/gist/gist.c index fdf0c5a..169566c 100644 --- a/src/backend/access/gist/gist.c +++ b/src/backend/access/gist/gist.c @@ -504,8 +504,24 @@ gistplacetopage(Relation rel, Size freespace, GISTSTATE *giststate, * gistRedoPageUpdateRecord() */ if (OffsetNumberIsValid(oldoffnum)) - PageIndexTupleDelete(page, oldoffnum); - gistfillbuffer(page, itup, ntup, InvalidOffsetNumber); + { + /* if we have just one tuple to update we replace it in-place on page */ + if(ntup == 1) + { + PageIndexTupleOverwrite(page, oldoffnum, *itup); + } + else + { + /* this corner case is here to support mix calls case (see comment above) */ + PageIndexTupleDelete(page, oldoffnum); + gistfillbuffer(page, itup, ntup, InvalidOffsetNumber); + } + } + else + { + /* just append all tuples at the end of a page */ + gistfillbuffer(page, itup, ntup, InvalidOffsetNumber); + } MarkBufferDirty(buffer); diff --git a/src/backend/access/gist/gistxlog.c b/src/backend/access/gist/gistxlog.c index b48e97c..934d50d 100644 --- a/src/backend/access/gist/gistxlog.c +++ b/src/backend/access/gist/gistxlog.c @@ -80,9 +80,26 @@ gistRedoPageUpdateRecord(XLogReaderState *record) page = (Page) BufferGetPage(buffer); - /* Delete old tuples */ - if (xldata->ntodelete > 0) + /* In case of single tuple update GiST calls overwrite + * In all other cases function gistplacetopage deletes + * old tuples and place updated at the end + * */ + if ( xldata->ntodelete == 1 && xldata->ntoinsert == 1 ) { + IndexTuple itup; + OffsetNumber offnum = *((OffsetNumber *) data); + data += sizeof(OffsetNumber); + itup = (IndexTuple) data; + PageIndexTupleOverwrite(page,offnum,itup); + /* set up data pointer to skip PageAddItem loop */ + data +=IndexTupleSize(itup); + Assert(data - begin == datalen); + /* correct insertion count for following assert check */ + ninserted++; + } + else if (xldata->ntodelete > 0) + { + /* if it's not in-place update we proceed with deleting old tuples */ OffsetNumber *todelete = (OffsetNumber *) data; data += sizeof(OffsetNumber) * xldata->ntodelete; @@ -115,6 +132,7 @@ gistRedoPageUpdateRecord(XLogReaderState *record) } } + /* check that buffer had exactly same count of tuples */ Assert(ninserted == xldata->ntoinsert); PageSetLSN(page, lsn); diff --git a/src/backend/storage/page/bufpage.c b/src/backend/storage/page/bufpage.c index f2a07f2..b196a05 100644 --- a/src/backend/storage/page/bufpage.c +++ b/src/backend/storage/page/bufpage.c @@ -707,6 +707,112 @@ PageGetHeapFreeSpace(Page page) /* + * PageIndexTupleOverwrite + * + * This routine does the work of overwriting a tuple on an index page. + * + * Unlike heap pages, we keep compacted line pointers. + */ +void +PageIndexTupleOverwrite(Page page, OffsetNumber offnum, Item newtup) +{ + PageHeader phdr = (PageHeader) page; + char *addr; + ItemId tupid; + int size_diff; + int oldsize; + unsigned offset; + int newsize; + unsigned alignednewsize; + int itemcount; + + /* + * As with PageIndexTupleDelete, paranoia is told to be justified. + */ + if (phdr->pd_lower < SizeOfPageHeaderData || + phdr->pd_lower > phdr->pd_upper || + phdr->pd_upper > phdr->pd_special || + phdr->pd_special > BLCKSZ) + ereport(ERROR, + (errcode(ERRCODE_DATA_CORRUPTED), + errmsg("corrupted page pointers: lower = %u, upper = %u, special = %u", + phdr->pd_lower, phdr->pd_upper, phdr->pd_special))); + + + + itemcount = PageGetMaxOffsetNumber(page); + if ((int) offnum <= 0 || (int) offnum > itemcount) + elog(ERROR, "invalid index offnum: %u", offnum); + + tupid = PageGetItemId(page, offnum); + /* set the item pointer */ + + + Assert(ItemIdHasStorage(tupid)); + + newsize = IndexTupleSize(newtup); + alignednewsize = MAXALIGN(newsize); + oldsize = ItemIdGetLength(tupid); + /* may have negative size here if new tuple is larger */ + size_diff = oldsize - alignednewsize; + offset = ItemIdGetOffset(tupid); + + + if (offset < phdr->pd_upper || (offset + size_diff) > phdr->pd_special || + offset != MAXALIGN(offset) || size_diff != MAXALIGN(size_diff)) + ereport(ERROR, + (errcode(ERRCODE_DATA_CORRUPTED), + errmsg("corrupted item offset: offset = %u, size = %u", + offset, (unsigned int) size_diff))); + + + /* + * Now move everything between the old upper bound (beginning of tuple + * space) and the end of the overwritten tuple forward, so that space in + * the middle of the page is left free. If we've just deleted the tuple + * at the beginning of tuple space, then there's no need to do the copy + * (and bcopy on some architectures SEGV's if asked to move zero bytes). + */ + + /* beginning of tuple space */ + addr = (char *) page + phdr->pd_upper; + + /* skip modifications if nothing has to be changed actually */ + if (size_diff != 0) + { + int i; + memmove(addr + size_diff, addr, (int) (offset - phdr->pd_upper)); + + /* adjust free space boundary pointers */ + phdr->pd_upper += size_diff; + + /* + * we need to adjust the linp entries that remain. + * + * Anything that used to be before the deleted tuple's data was moved + * forward by the size of the deleted tuple. + */ + + for (i = FirstOffsetNumber; i <= itemcount; i++) + { + ItemId ii = PageGetItemId(phdr, i); + + Assert(ItemIdHasStorage(ii)); + if (ItemIdGetOffset(ii) <= offset) + ii->lp_off += size_diff; + } + } + + /* Fix updated tuple length */ + ItemIdSetNormal(tupid, offset + size_diff, newsize); + + + /* now place new tuple on page */ + memmove(PageGetItem(page,tupid), newtup, newsize); +} + + +/* * PageIndexTupleDelete * * This routine does the work of removing a tuple from an index page. diff --git a/src/include/storage/bufpage.h b/src/include/storage/bufpage.h index 15cebfc..780e982 100644 --- a/src/include/storage/bufpage.h +++ b/src/include/storage/bufpage.h @@ -426,6 +426,7 @@ extern Size PageGetFreeSpace(Page page); extern Size PageGetExactFreeSpace(Page page); extern Size PageGetHeapFreeSpace(Page page); extern void PageIndexTupleDelete(Page page, OffsetNumber offset); +extern void PageIndexTupleOverwrite(Page page, OffsetNumber offnum, Item newtup); extern void PageIndexMultiDelete(Page page, OffsetNumber *itemnos, int nitems); extern void PageIndexDeleteNoCompact(Page page, OffsetNumber *itemnos, int nitems); -- 2.7.0.windows.1